# comun self hosted implementation: bytecode extra library
# module prefix: e
#
# This module contains additional resources for handling the implementation
# specific comun BC. This module is separated from the basic BC module so that
# programs not needing advanced BC manipulation aren't burdened with compiling
# this extra code, which might increase RAM requirements (even if this code is
# later optimized out).

~"cmn_bytecode.cmn"

# arg: opcode
# ret: char3 char2 char1
# Converts instr opcode to three letter mnemonic.
CMN_eOpcodeToMnemo:
  # TODO: this all could be further minimized, e.g. make a helper func for
  # pushing a series of '?'s

  # TODO: move this whole func elsewhere to make this module smaller?

  $0>_CMN_gP1

  $0 16 % 3 * ++ ++
  >< 16 /
           #   f  e  d  c  b  a  9  8  7  6  5  4  3  2  1  0
    $0 0 = ?  "INI??????JMAJNAJIARETCAECAL??????ERRCOCDESNOPEND"
  ; $0 1 = ?  "OUTPOPTRASWPCNDCON??????PUXPCMMGEMEXPCOPAXPACPSC"
  ; $0 2 = ?  "??????DICDIX??????MUCMUX??????SUCSUX??????ADCADX"
  ; $0 3 = ?  "??????????????????MSCMSX??????MOCMOX??????DSCDSX"
  ; $0 4 = ?  "??????SECSEX??????SMCSMX??????GECGEX??????GRCGRX"
  ; $0 5 = ?  "??????LSCLSX??????SSCSSX??????BSCBSX??????GSCGSX"
  ; $0 6 = ?  "??????BOCBOX??????BACBAX??????NECNEX??????EQCEQX"
  ; $0 7 = ?  "??????LXCLXX??????LOCLOX??????LACLAX??????BXCBXX"
  ; $0 8 = ?  "??????????????????SLCSLX??????SRCSRX???BNO??????"
  ; $0 15 = ? "INP?????????INU?????????????????????ADR?????????"
  ;
    $_CMN_gP1>0
    ^
    "???"
    !.
  . . . . . . . . . .
 
  $_CMN_gP1>0

  $+_CMN_gP1
 
  $_CMN_gP1 $>_CMN_gP1
  $_CMN_gP1 $>_CMN_gP1
  $_CMN_gP1
.

# arg: 3ptrI
# ret: 3ptrTableI
# Converts ptr index as encoded in an instr to an index in PT, doesn't check
# index validity.
CMN_ePtrIToPtrTableI: ~32 $0 15 > >< 15 - 0 ?? ~0 .

# arg: pc0 ss0 pc1 ss1 pc2 ss2 pc3 ss3
# Analyzes loaded BC, pcN is exact num of ptrs used in TE N (num of user ptrs
# plus ptr 0, excluding ptrs 1, 2, ...) and ssN is estimated min. stack size (in
# cells) in TE N. If TE N isn't used by the BC, pcN and ssN will be both 0.
CMN_eAnalyze:
  ~32 0 CMN_bCurrentInstrSetAddr ~0
  0 0 0 0 0 0 0 0 # results

  1 @
    CMN_bCurrentInstrExtractPtrs

    $0 -1 != ?
      CMN_gMaxS
      CMN_ePtrIToPtrTableI # TODO: WTF THIS CAN'T BE HERE

      $0>_CMN_gP1
      3 CMN_bCurrentInstrTypeEnv - -2 * -2 + $+_CMN_gP1
      $_CMN_gP1 CMN_gMax $:_CMN_gP1
    ;
      ^ ^
    .

    CMN_bCurrentInstrGet

    # setting pointer to high addr, we simply add the current esitmate to this
    # addr -- this is not accurate and can overestimate mem if many ptrs are set
    # a high addr
    $0 CMN_bInstrOpcode CMN_bOPCODE_PSC = ?
      CMN_bCurrentInstrGetConsts ^
      $0>_CMN_gP1
      3 CMN_bCurrentInstrTypeEnv - -2 * 2 - $+_CMN_gP1
      $_CMN_gP1 + $:_CMN_gP1
    .

    $0 CMN_bInstrAffectsMems

    $0 +b1000 & ? $2 ++ $:3 .
    $0 +b0100 & ? $4 ++ $:5 .
    $0 +b0010 & ? $6 ++ $:7 .
       +b0001 & ? $7 ++ $:8 .

    ~16
       $>CMN_bCurrentInstr
    ~0
  .

  # add 1 ptr (ST) to ptr count in each TE actually used
  $7 $7 | 0 != $8 + $:8
  $5 $5 | 0 != $6 + $:6
  $3 $3 | 0 != $4 + $:4
  $1 $1 | 0 != $2 + $:2
.

# arg: instr
# ret: 0 sn ... s2 s1 s0
# Converts a 16 bit instr to a zero terminated human readable string.
CMN_eInstrToStr:
  $:_CMN_gV1

  0 # string terminator 

  $_CMN_gV1 CMN_bInstrConstContinues ">" " " ??
  $_CMN_gV1 CMN_bInstrWontPop "'" " " ??
  $_CMN_gV1 +xff & CMN_eOpcodeToMnemo " "

  $_CMN_gV1 CMN_bInstrTypeEnv # TE string
  $0 3 = + 8 *    
  $0 10 % "0" + >< 10 / "0" +

  "   "

  16  # to binary string
  @'
    --

    $0 7 = ?
      ";" ><
    .

    $_CMN_gV1 15 $2 - |> 1 & "0" + ><
  .
  ^
.

# ret: succ
# Loads BC from input to CMN_bBytecode, returns 1 (OK) or 0 (error: BC too big).
CMN_eLoadBC:
  ~32 CMN_bBYTECODE_SIZE ~0
  ~16 $CMN_bBytecode>_CMN_g2P2 ~0
  ~32
    @' # set _CMN_g2P2 to the end of BC mem
      ~16 $>_CMN_g2P2 ~32
      --
    .
    ^
  ~0

  ~16
    $CMN_bBytecode>_CMN_g2P1

    0 ~0 CMN_bHEADER_SIZE 2 / >16 ~16 $:_CMN_g2V1

    @@
      <? !! ?
        ~0 1 ~16
        !@
      .

      <- <- 8 |< |
      
      $:_CMN_g2P1

      $_CMN_g2V1 ?
        $_CMN_g2V1 -- $:_CMN_g2V1
      ;
        $_CMN_g2P1 !! ? # END instr => stop reading
          ~0 1 ~16
          !@
        .
      .

      $_CMN_g2P1=_CMN_g2P2 !! ?
        ~0 0 ~16 # error, BC too big
        !@
      .

      $>_CMN_g2P1
    .
  ~0
.
